﻿using System;
using System.Windows.Navigation;
using Microsoft.Practices.Prism.Regions;
using NavigationContext = Microsoft.Practices.Prism.Regions.NavigationContext;

namespace UltimateNavigationFramework.UriNavigation
{
    public class UltimateRegionNavigationCommand : IUltimateRegionNavigationCommand
    {
        private UltimateNavigationFrame _frame;
        private NavigationContext _prismNavigationContext;
        private Uri _source;
        private Action<NavigationResult> _navigationCallback;
        private Action<NavigationContext> _raiseNavigating;
        private Action<NavigationContext> _raiseNavigated;
        private Action<NavigationContext, Exception> _raiseNavigatingFailed;

        public UltimateRegionNavigationCommand(UltimateNavigationFrame frame,
                                        Uri source,
                                        NavigationContext prismNavigationContext,
                                        Action<NavigationResult> navigationCallback,
                                        Action<NavigationContext> raiseNavigating,
                                        Action<NavigationContext> raiseNavigated,
                                        Action<NavigationContext, Exception> raiseNavigatingFailed)
        {
            this._frame = frame;
            this._source = source;
            this._prismNavigationContext = prismNavigationContext;
            this._navigationCallback = navigationCallback;
            this._raiseNavigating = raiseNavigating;
            this._raiseNavigated = raiseNavigated;
            this._raiseNavigatingFailed = raiseNavigatingFailed;

            this.HookNavigation();
        }

        public void RequestNavigate()
        {
            this._frame.Navigate(this._source);
        }

        private void HookNavigation()
        {
            this._frame.Navigated += this.OnFrameNavigated;
            this._frame.NavigationFailed += this.OnFrameNavigationFailed;
            this._frame.NavigationStopped += this.OnFrameNavigationStopped;
        }

        private void UnhookNavigationEvents()
        {
            this._frame.Navigated -= this.OnFrameNavigated;
            this._frame.NavigationFailed -= this.OnFrameNavigationFailed;
            this._frame.NavigationStopped -= this.OnFrameNavigationStopped;
        }

        private void OnFrameNavigated(Object sender, NavigationEventArgs args)
        {
            this.UnhookNavigationEvents();

            this._raiseNavigating(this._prismNavigationContext);
            this._navigationCallback(new NavigationResult(this._prismNavigationContext, true));
            this._raiseNavigated(this._prismNavigationContext);
        }

        private void OnFrameNavigationFailed(Object sender, UltimateNavigationFailedEventArgs args)
        {
            this.UnhookNavigationEvents();

            args.Handled = true;

            var navigationResult = new NavigationResult(this._prismNavigationContext, args.Exception);

            this._navigationCallback(navigationResult);

            this._raiseNavigatingFailed(this._prismNavigationContext, args.Exception);
        }

        private void OnFrameNavigationStopped(Object sender, NavigationEventArgs args)
        {
            this.UnhookNavigationEvents();
        }
    }
}
